var Characters = {
	Collection: function() {
		var me = this;

		this.Items = {};

		this.Add = function(params) {
			var item = new Characters.Character(params);
			me.Items[item.UniqueId] = item;
			return item;
		};
	},
	Character: function(params) {
		var me = this;

		var level = Characters.Levels[params.level];
		var race = Characters.Races[params.race];
		var job = Characters.Jobs[params.job];

		var moves = {};
		var combos = {};
		var friendly_moves = {};

		for ( var i = 1; i <= params.level; i++ ) {
			for ( var key in job[i].moves ) {
				moves[key] = $.extend(Abilities.Moves[key], job[i].moves[key], params.moves[key], {caster: me});
			}

			for ( var key in job[i].combos ) {
				combos[key] = $.extend(Abilities.Combos[key], job[i].combos[key], params.combos[key], {caster: me});
			}

			for ( var key in job[i].friendly_moves ) {
				friendly_moves[key] = $.extend(Abilities.FriendlyMoves[key], job[i].friendly_moves[key], params.friendly_moves[key], {caster: me});
			}
		}

		params = $.extend(params, job[params.level], race[params.level], level);
		params.moves = moves;
		params.combos = combos;
		params.friendly_moves = friendly_moves;

		/* ----------------
		   TEMPORARY
		---------------- */
		var ai = function() {
			me.QueueAbility('punch');
			me.QueueAbility('punch');
			me.QueueAbility('punch');
			me.QueueAbility('punch');
			me.QueueAbility('punch');
			me.ApplyQueue();
		};

		this.Ai = function() {
			var tai = function() {
				ai();
				$this.unbind('afterdamageapplied', tai);
			};

			$this.bind('afterdamageapplied', tai);
		};
		/* ----------------
		   /TEMPORARY
		---------------- */

		var d = new Date();
		this.UniqueId = 'Character' + d.getTime();

		this.Name               = params.name;
		this.Race               = params.race;
		this.Job                = params.job;
		this.Level              = params.level;
		this.AbilityCount       = params.ability_count;
		this.EnhancementCount   = params.enhancement_count;
		this.AvailableAbilities = params.available_abilities;
		this.Affiliation        = params.affiliation;
		this.Attrs = {
			Armor:	   params.attributes.armor,
			Power:	   params.attributes.power,
			HitPoints: params.attributes.hit_points
		};
		this.MaxQueue           = params.max_queue;
		this.BaseStats = {
			Armor:	   this.Attrs.Armor,
			Power:	   this.Attrs.Power,
			HitPoints: this.Attrs.HitPoints
		};
		this.ModStats = {
			Armor:	   this.BaseStats.Armor,
			Power:	   this.BaseStats.Power,
			HitPoints: this.BaseStats.HitPoints
		};
		this.HitPoints = this.ModStats.HitPoints;

		this.Target       = null;
		this.Gear         = new Effects.Collection(me);
		this.Buffs        = new Effects.Collection(me);
		this.Debuffs      = new Effects.Collection(me);

		this.Moves        = new Abilities.Collection(me);
		this.Combos       = new Abilities.Collection(me);
		this.FriendlyMoves= new Abilities.Collection(me);
		this.Enhancements = new Enhancements.Collection(me);

		this.HasAny = function(name) {
			return ( me.Gear.HasAny(name) || me.Buffs.HasAny(name) || me.Debuffs.HasAny(name) );
		};

		this.GetAll = function(name) {
			return ( me.Gear.GetAll(name) + me.Buffs.GetAll(name) + me.Debuffs.GetAll(name) );
		};

		this.Enhance = function(name) {
			return me.Enhancements.Add(name);
		}

		this.IsHostile = function(character) {
			return ( me.Affiliation != character.Affiliation );
		};

		this.IsFriendly = function(character) {
			return ( me.Affiliation == character.Affiliation );
		};

		/*this.SetTarget = function(character_name) {
			target = characters[character_name];
		};*/

		this.ApplyDamage = function(damage) {
			me.HitPoints -= damage;
			Log({type: 'Characters.Character.ApplyDamage', character: me});
		};

		var effects = [this.Gear, this.Buffs, this.Debuffs];
		this.UpdateStats = function() {
			me.ModStats = {
				Armor:	   me.BaseStats.Armor,
				Power:	   me.BaseStats.Power,
				HitPoints: me.BaseStats.HitPoints
			};

			for ( var i = 0, effect; effect = effects[i++], i < effects.length; ) {
				me.ModStats = {
					Armor:	   me.ModStats.Armor * (1 + effect.GetAll('ArmorMultiplyBase')),
					Power:	   me.ModStats.Power * (1 + effect.GetAll('PowerMultiplyBase')),
					HitPoints: me.ModStats.HitPoints * (1 + effect.GetAll('HitPointsMultiplyBase'))
				};
			}

			for ( var i = 0, effect; effect = effects[i++], i < effects.length; ) {
				me.ModStats = {
					Armor:	   me.ModStats.Armor + effect.GetAll('ArmorAdd'),
					Power:	   me.ModStats.Power + effect.GetAll('PowerAdd'),
					HitPoints: me.ModStats.HitPoints + effect.GetAll('HitPointsAdd')
				};
			}

			for ( var i = 0, effect; effect = effects[i++], i < effects.length; ) {
				me.ModStats = {
					Armor:	   me.ModStats.Armor * (1 + effect.GetAll('ArmorMultiplyTotal')),
					Power:	   me.ModStats.Power * (1 + effect.GetAll('PowerMultiplyTotal')),
					HitPoints: me.ModStats.HitPoints * (1 + effect.GetAll('HitPointsMultiplyTotal'))
				};
			}
		};

		this.Queue = [];

		this.QueueAbility = function(name) {
			var item;
			if ( me.Queue.length >= me.MaxQueue ) {
				return false;
			} else if ( !( item = me.Moves.GetByName(name) ) ) {
				return false;
			}

			me.Queue[me.Queue.length] = item;
			Log({type: 'Characters.Character.QueueAbility', Item: item, Character: me});
			return true;
		};

		this.OffenseRoll = function(type) {
			return Math.random();
		};

		this.DefenseRoll = function(type) {
			return Math.random();
		};

		this.CastingAbility = null;
		this.AbilityTimeout = null;
		var queue_string;
		var running_queue = false;
		var queue;
		var run_queue = function() {
			running_queue = true;
			var ability;
			if ( queue.length ) {
				ability = queue.splice(0,1)[0];
				me.CastingAbility = ability;
				ability.Cast(run_queue);
			} else if ( queue_string ) {
				ability = me.Combos.Get(queue_string);
				queue_string = null;
				ability.Cast(run_queue);
			} else {
				running_queue = false;
				me.CastingAbility = null;
				me.AbilityTimeout = null;
				queue = [];
			}
		};

		this.Counter = function(counter) {
			if ( counter == 'Parry' ) {
				me.CastingAbility = null;
				queue_string = null;
			} else if ( counter == 'Dodge' ) {
				me.CastingAbility = null;
				queue_string = null;
			} else if ( counter == 'Block' ) {
				me.CastingAbility = null;
				queue_string = null;
			} else if ( counter == 'Stop' ) {
				me.CastingAbility = null;
				queue_string = null;
				me.Queue = [];
			} else if ( counter == 'Stun' ) {
				me.CastingAbility = null;
				queue_string = null;
				me.Queue = [];
			} else if ( counter == 'Knockdown' ) {
				me.CastingAbility = null;
				queue_string = null;
				me.Queue = [];
			}
		};

		this.ApplyQueue = function(method) {
			if ( running_queue ) return;
			queue = [];
			for ( var i = 0; i < me.Queue.length; i++ ) {
				queue[queue.length] = me.Queue[i].Id;
			}
			queue_string = queue.toString();

			if ( method == 'self' ) {
				var combo = me.FriendlyMoves.Get(queue_string);
				if ( !combo ) {
					queue_string = null;
					return false;
				} else {
					combo.Cast(null, method);
				}
			} else {
				queue = me.Queue;
				var combo = me.Combos.Get(queue_string);
				if ( !combo ) queue_string = null;
				run_queue();
			}
			me.ClearQueue();
		};

		this.ClearQueue = function() {
			me.Queue = [];
			Log({type: 'Characters.Character.ClearQueue', character: me});
		}

		for ( var ability in params.moves ) {
			me.Moves.Add(params.moves[ability]);
		}

		for ( var ability in params.combos ) {
			me.Combos.Add(params.combos[ability]);
		}

		for ( var ability in params.friendly_moves ) {
			me.FriendlyMoves.Add(params.friendly_moves[ability]);
		}

		this.UpdateStats();

		Log({type: 'Characters.Character', character: me, params: params});
	},
	Levels: {
		1: {
			max_queue: 2,
			active_enhancement_count: 1
		}
	},
	Races: {
		Human: {
			1: {}
		}
	},
	Jobs: {
		Squire: {
			1: {
				enhancement_count: 1,
				attributes: {
					armor:     1,
					power:     1,
					hit_points: 100
				},
				moves: {
					punch: {},
					focus: {},
					grab: {}
				},
				combos: {
					'punch': {},
					'punch,punch': {}
				},
				friendly_moves: {
					'focus,punch': {}
				}
			}
		}
	},
	Toons: {
		Maclovio: {
			name: 'Maclovio',
			race: 'Human',
			job: 'Squire',
			level: 1,
			affiliation: 'goodie',
			moves: {
				punch: {
					level: 1,
					position: 1
				},
				focus: {
					level: 1,
					position: 2
				},
				grab: {
					level: 1,
					position: 3
				}
			},
			combos: {
				'punch': {
					level: 1,
					position: 1
				},
				'punch,punch': {
					level: 1,
					position: 2
				}
			},
			friendly_moves: {
				'focus,punch': {
					level: 1,
					position: 3
				}
			}
		},
		Mobby: {
			name: 'Mobby',
			race: 'Human',
			job: 'Squire',
			level: 1,
			affiliation: 'baddie',
			moves: {
				punch: {
					level: 1,
					position: 1
				},
				focus: {
					level: 1,
					position: 2
				},
				grab: {
					level: 1,
					position: 3
				}
			},
			combos: {},
			friendly_moves: {}
		}
	}
};
